﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TasksModel
{
    public class AutosaveManager
    {
        /// <summary>
        /// Invoked on any change in TaskLibrary object graph.
        /// </summary>
        public event EventHandler AutoSave;

        /// <summary>
        /// Start invoking AutoSave event on any change in object tree.
        /// </summary>
        /// <param name="tl"></param>
        public void Attach(TasksModel.TaskLibrary tl)
        {
            System.ComponentModel.PropertyChangedEventHandler pceh =
                new System.ComponentModel.PropertyChangedEventHandler(tl_PropertyChanged);
            System.Collections.Specialized.NotifyCollectionChangedEventHandler ncceh =
                new System.Collections.Specialized.NotifyCollectionChangedEventHandler(tl_CollectionChanged);
            RemoveEvents(tl, pceh, ncceh);
            AddEvents(tl, pceh, ncceh);
        }

        /// <summary>
        /// Stop invoking AutoSave event on any change in object tree.
        /// </summary>
        /// <param name="tl"></param>
        public void Detach(TasksModel.TaskLibrary tl)
        {
            System.ComponentModel.PropertyChangedEventHandler pceh =
                   new System.ComponentModel.PropertyChangedEventHandler(tl_PropertyChanged);
            System.Collections.Specialized.NotifyCollectionChangedEventHandler ncceh =
                new System.Collections.Specialized.NotifyCollectionChangedEventHandler(tl_CollectionChanged);
            RemoveEvents(tl, pceh, ncceh);
        }

        /// <summary>
        /// Use after new object is added to collection.
        /// </summary>
        /// <param name="instance"></param>
        public void AttachExplicit(System.ComponentModel.INotifyPropertyChanged instance)
        {
            instance.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(tl_PropertyChanged);
        }

        /// <summary>
        /// Use after new collection is assigned.
        /// </summary>
        /// <param name="instance"></param>
        public void AttachExplicit(System.Collections.Specialized.INotifyCollectionChanged instance)
        {
            instance.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(tl_CollectionChanged);
        }

        /// <summary>
        /// Use before object is removed from collection.
        /// </summary>
        /// <param name="instance"></param>
        public void DetachExplicit(System.ComponentModel.INotifyPropertyChanged instance)
        {
            instance.PropertyChanged -= new System.ComponentModel.PropertyChangedEventHandler(tl_PropertyChanged);
        }

        /// <summary>
        /// Use before collection is removed from object.
        /// </summary>
        /// <param name="instance"></param>
        public void DetachExplicit(System.Collections.Specialized.INotifyCollectionChanged instance)
        {
            instance.CollectionChanged -= new System.Collections.Specialized.NotifyCollectionChangedEventHandler(tl_CollectionChanged);
        }

        #region Private

        private void AddEvents(
            TasksModel.TaskLibrary tl,
            System.ComponentModel.PropertyChangedEventHandler pceh,
            System.Collections.Specialized.NotifyCollectionChangedEventHandler ncceh)
        {
            tl.PropertyChanged += pceh;
            if (tl.SynchSettings != null)
                tl.SynchSettings.PropertyChanged += pceh;
            AddEvents(tl.Tasks.UnimportantUrgentTasks, pceh, ncceh);
            AddEvents(tl.Tasks.UnimportantNotUrgentTasks, pceh, ncceh);
            AddEvents(tl.Tasks.ImportantUrgentTasks, pceh, ncceh);
            AddEvents(tl.Tasks.ImportantNotUrgentTasks, pceh, ncceh);
            AddEvents(tl.Tasks.Archived, pceh, ncceh);
            tl.Statuses.CollectionChanged += ncceh;
            tl.Flags.CollectionChanged += ncceh;
        }

        private void AddEvents(
            TasksModel.TaskList taskList,
            System.ComponentModel.PropertyChangedEventHandler pceh,
            System.Collections.Specialized.NotifyCollectionChangedEventHandler ncceh)
        {
            taskList.CollectionChanged += ncceh;
            foreach (TasksModel.Task t in taskList)
            {
                if (t.Flags != null)
                    t.Flags.CollectionChanged += ncceh;
                if (t.Attachments != null)
                    t.Attachments.CollectionChanged += ncceh;
                if (t.Dependencies != null)
                    t.Dependencies.CollectionChanged += ncceh;
                t.PropertyChanged += pceh;
            }
        }

        private void RemoveEvents(
            TasksModel.TaskLibrary tl,
            System.ComponentModel.PropertyChangedEventHandler pceh,
            System.Collections.Specialized.NotifyCollectionChangedEventHandler ncceh)
        {
            tl.PropertyChanged -= pceh;
            if (tl.SynchSettings != null)
                tl.SynchSettings.PropertyChanged -= pceh;
            RemoveEvents(tl.Tasks.UnimportantUrgentTasks, pceh, ncceh);
            RemoveEvents(tl.Tasks.UnimportantNotUrgentTasks, pceh, ncceh);
            RemoveEvents(tl.Tasks.ImportantUrgentTasks, pceh, ncceh);
            RemoveEvents(tl.Tasks.ImportantNotUrgentTasks, pceh, ncceh);
            RemoveEvents(tl.Tasks.Archived, pceh, ncceh);
            tl.Statuses.CollectionChanged -= ncceh;
            tl.Flags.CollectionChanged -= ncceh;
        }

        private void RemoveEvents(
            TasksModel.TaskList taskList,
            System.ComponentModel.PropertyChangedEventHandler pceh,
            System.Collections.Specialized.NotifyCollectionChangedEventHandler ncceh)
        {
            taskList.CollectionChanged -= ncceh;
            foreach (TasksModel.Task t in taskList)
            {
                if (t.Flags != null)
                    t.Flags.CollectionChanged -= ncceh;
                if (t.Attachments != null)
                    t.Attachments.CollectionChanged -= ncceh;
                if (t.Dependencies != null)
                    t.Dependencies.CollectionChanged -= ncceh;
                t.PropertyChanged -= pceh;
            }
        }

        private void tl_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (AutoSave != null)
                AutoSave(sender, e);
        }

        private void tl_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove)
                foreach (object o in e.OldItems)
                {
                    if (o is System.ComponentModel.INotifyPropertyChanged)
                        DetachExplicit((System.ComponentModel.INotifyPropertyChanged)o);
                    if (o is System.Collections.Specialized.INotifyCollectionChanged)
                        DetachExplicit((System.Collections.Specialized.INotifyCollectionChanged)o);
                }
            if (AutoSave != null)
                AutoSave(sender, e);
        }

        #endregion
    }
}